#!/usr/bin/env python
# coding: utf-8

# # 图数据集加载与处理
# luojianet_ms提供的`luojianet_ms.dataset`模块可以帮助用户构建数据集对象，分批次地读取文本数据。同时，在各个数据集类中还内置了数据处理和数据分词算子，使得数据在训练过程中能够像经过pipeline管道的水一样源源不断地流向训练系统，提升数据训练效果。
# 
# 本章将简要演示如何使用luojianet_ms加载和处理图数据。

# ## 图的概念
# 
# 在介绍图数据的读取及增强之前，先介绍图的基本概念进行，有助于后续内容更好地理解。通常一个图（graph) `G`是由一系列的节点(vertices) `V`以及边（eage）`E`组成的，每条边都连接着图中的两个节点，用公式可表述为：
# 
# $$G = F(V, E)$$
# 
# 简单的图如下所示。
# 
# ![basicGraph.png](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r1.7/tutorials/source_zh_cn/advanced/dataset/images/basic_graph.png)
# 
# 图中包含节点V = {a, b, c, d}，和边E = {(a, b), (b, c), (c, d), (d, b)}，针对图中的连接关系通常需借助数学的方式进行描述，如常用的基于邻接矩阵的方式，用于描述上述图连接关系的矩阵C如下，其中a、 b、c、d对应为第1、2高级数据集管理、 3图像处理、4个节点。
# 
# $$C=\begin{bmatrix}
# 1中低阶API实现深度学习&1中低阶API实现深度学习&0&0\\
# 1中低阶API实现深度学习&1中低阶API实现深度学习&1中低阶API实现深度学习&1中低阶API实现深度学习\\
# 0&1中低阶API实现深度学习&1中低阶API实现深度学习&1中低阶API实现深度学习\\
# 0&1中低阶API实现深度学习&1中低阶API实现深度学习&1中低阶API实现深度学习\\
# \end{bmatrix}$$

# ## 数据集准备环节
# 
# 1中低阶API实现深度学习. 数据集介绍
# 
# 常用的图数据集包含Cora、Citeseer、PubMed等，在本文中我们基于Cora数据集进行介绍。
# 
# > 原始数据集可以从[ucsc网站](https://linqs-data.soe.ucsc.edu/public/lbc/cora.tgz)进行下载，本文采用kimiyoung提供的[预处理后的版本](https://github.com/kimiyoung/planetoid)[[1中低阶API实现深度学习]](#参考文献)。
# 
# 其中，Cora数据集主体部分(`cora.content`)包含2708条样本，每条样本描述1篇科学论文的信息，论文都属于7个类别中的一个。每条样本数据包含三部分，依次为论文编号、论文的词向量（一个1433位的二进制）、论文的类别；引用数据集部分(`cora.cites`)包含5429行，每行包含两个论文编号，表示第二篇论文对第一篇论文进行了引用。
# 
# 2高级数据集管理. 数据集下载
# 
# 以下示例代码将cora数据集下载并解压到指定位置：

# In[1中低阶API实现深度学习]:


import os
'''
数据下载手动完成
https://github.com/kimiyoung/planetoid
'''
# 下载预处理后的cora数据集目录如下所示。
# 
# ```text
# ./cora
# ├── ind.cora.allx
# ├── ind.cora.ally
# ├── ind.cora.graph
# ├── ind.cora.test.index
# ├── ind.cora.tx
# ├── ind.cora.ty
# ├── ind.cora.x
# ├── ind.cora.y
# ├── trans.cora.graph
# ├── trans.cora.tx
# ├── trans.cora.ty
# ├── trans.cora.x
# └── trans.cora.y
# ```
# In[2高级数据集管理]:
# 数据集格式转换
# 
# 将数据集转换为luojianet_ms Record格式，可借助models仓库提供的转换脚本进行转换，生成的luojianet_ms Record文件在`./cora_mindrecord`路径下。
# 这里提供已经转换好格式的数据集

#处理后的cora数据集目录如下所示
# ./cora_mindrecord
# ├── cora_mr
# ├── cora_mr.db

# ## 加载数据集
# 
# luojianet_ms目前支持加载文本领域常用的经典数据集和多种数据存储格式下的数据集，用户也可以通过构建自定义数据集类实现自定义方式的数据加载。
# 
# 下面演示使用`luojianet_ms.dataset`模块中的`MindDataset`类加载上述已转换成luojianet_ms Record格式的cora数据集。
# 
# 1中低阶API实现深度学习. 配置数据集目录，创建数据集对象。

# In[3图像处理]:


import luojianet_ms.dataset as ds
import numpy as np

data_file = "./cora_mindrecord/cora_mr"
dataset = ds.GraphData(data_file)


# 2高级数据集管理. 访问对应的接口，获取图信息及特性、标签内容。

# In[4自然语言]:


# 查看图中结构信息
graph = dataset.graph_info()
print("graph info:", graph)

# 获取所有的节点信息
nodes = dataset.get_all_nodes(0)
nodes_list = nodes.tolist()
print("node shape:", len(nodes_list))

# 获取特征和标签信息，总共2708条数据
# 每条数据中特征信息是用于描述论文i，长度为1433的二进制表示，标签信息指的是论文所属的种类
raw_tensor = dataset.get_node_feature(nodes_list, [1, 2])
features, labels = raw_tensor[0], raw_tensor[1]

print("features shape:", features.shape)
print("labels shape:", labels.shape)
print("labels:", labels)


# ## 数据处理
# 
# 下面演示构建pipeline，对节点进行采样等操作。
# 
# 1中低阶API实现深度学习. 获取节点的邻居节点，构造邻接矩阵。

# In[5]:


neighbor = dataset.get_all_neighbors(nodes_list, 0)

# neighbor的第一列是node_id，第二列到最后一列存储的是第一列的邻居节点，如果不存在这么多，则用-1补齐。
print("neighbor:\n", neighbor)


# 2高级数据集管理. 依据节点的邻居节点信息，构造邻接矩阵。

# In[6]:


nodes_num = labels.shape[0]
node_map = {node_id: index for index, node_id in enumerate(nodes_list)}
adj = np.zeros([nodes_num, nodes_num], dtype=np.float32)

for index, value in np.ndenumerate(neighbor):
    # neighbor的第一列是node_id，第二列到最后一列存储的是第一列的邻居节点，如果不存在这么多，则用-1补齐。
    if value >= 0 and index[1] > 0:
        adj[node_map[neighbor[index[0], 0]], node_map[value]] = 1

print("adj:\n", adj)


#  节点采样，支持常见的多次跳跃采样与随机游走采样方法等。
# - 多跳邻接点采样如（a）图所示，当次采样的节点将作为下次采样的起始点；随机游走方式如（b）图所示，随机选择一条路径依次遍历相邻的节点，对应图中则选择了从V<sub>i</sub>到V<sub>j</sub>的游走路径。
# 
# ![graph](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r1.7/tutorials/source_zh_cn/advanced/dataset/images/graph_sample.png)

# In[7]:


# 基于多次跳跃进行节点采样
neighbor = dataset.get_sampled_neighbors(node_list=nodes_list[0:21], neighbor_nums=[2], neighbor_types=[0])
print("neighbor:\n", neighbor)

# 基于随机游走进行节点采样
meta_path = [0]
walks = dataset.random_walk(nodes_list[0:21], meta_path)
print("walks:\n", walks)


# > 由于上面代码中游走采样存在随机性，因此在执行时可能会出现不同的打印结果。
# 
# 4自然语言. 通过节点获取边/通过边获取节点。

# In[8]:


# 通过边获取节点
part_edges = dataset.get_all_edges(0)[:10]
nodes = dataset.get_nodes_from_edges(part_edges)
print("part edges:", part_edges)
print("nodes:", nodes)

# 通过节点获取边
nodes_pair_list = [(0, 633), (1, 652), (2, 332), (3, 2544)]
edges = dataset.get_edges_from_nodes(nodes_pair_list)
print("edges:", edges)


# ## 参考文献
# 
# [1中低阶API实现深度学习] Yang Z, Cohen W, Salakhudinov R. [Revisiting semi-supervised learning with graph embeddings](http://proceedings.mlr.press/v48/yanga16.pdf).
